import { DBConfig } from "./DBConfig";
import mongodb, { WithId } from "mongodb";

const mongodb1 = require("mongodb");
const MongoClient = mongodb1.MongoClient;

import { DBI } from "./db";

class Db implements DBI {
  public client: mongodb.MongoClient | undefined;
  static instance: Db | null;

  static getInstance() {
    if (!Db.instance) this.instance = new Db();
    return this.instance;
  }

  constructor() {
    this.connection();
  }

  connection(): Promise<mongodb.MongoClient> {
    return new Promise<mongodb.MongoClient>((resolve, reject) => {
      if (!this.client) {
        MongoClient.connect(
          DBConfig.url,
          {},
          (err: any, client: mongodb.MongoClient | undefined) => {
            if (err) {
              reject(err);
            } else {
              // console.log("db server start success!");
              this.client = client;
              resolve(client as mongodb.MongoClient);
            }
          }
        );
      } else {
        resolve(this.client);
      }
    });
  }

  insert<T>(collectionName: string, doc: T): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        const result = db
          .collection(collectionName)
          .insertOne(doc, (err, result) => {
            if (err) {
              resolve(false);
              reject(err);
            } else {
              resolve(true);
            }
          });
      });
    });
  }

  insertMany<T>(collectionName: string, docs: T[]): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        const result = db
          .collection(collectionName)
          .insertMany(docs, (err, result) => {
            if (err) {
              resolve(false);
              reject(err);
            } else {
              resolve(true);
            }
          });
      });
    });
  }

  delete(collectionName: string, filter: object): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        db.collection(collectionName).deleteOne(filter, (err, result) => {
          if (err) {
            resolve(false);
            reject(err);
          } else {
            resolve(true);
          }
        });
      });
    });
  }

  update(
    collectionName: string,
    filter: object,
    update: object
  ): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        db.collection(collectionName).updateOne(
          filter,
          { $set: update },
          (err, result) => {
            if (err) {
              resolve(false);
              reject(err);
            } else {
              // console.log(filter);
              // console.log(update);
              resolve(true);
            }
          }
        );
      });
    });
  }

  // 使用函数泛型来约束返回结果
  find(collectionName: string, filter: object): Promise<any[]> {
    return new Promise<any[]>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        const result = db.collection(collectionName).find(filter);
        result.toArray((err, docs) => {
          if (err) {
            reject(err);
          } else {
            resolve(docs as WithId<Document>[]);
          }
        });
      });
    });
  }

  aggregate(collectionName: string, pipeline: object[]): Promise<any[]> {
    return new Promise<any[]>((resolve, reject) => {
      this.connection().then((client) => {
        const db = client.db();
        db.collection(collectionName)
          .aggregate(pipeline)
          .toArray((err, docs) => {
            if (err) {
              reject(err);
            } else {
              resolve(docs as Document[]);
            }
          });
      });
    });
  }
}

const DBHandler = new Db();

export { DBHandler };
